package model {
	import model.BitBoard;
	/**
	 * ...
	 * @author flyingSnow
	 */
	public final class BitBoard {
		private var _bitArray:Array;
		private var _numSquares:uint=0;
		public static const BOARD_WIDTH:int = 20;
		public function BitBoard() {
			_bitArray = [];
			for (var i:int = 0; i < BOARD_WIDTH; i++ ) {
				_bitArray.push(0);
			}
		}
		
		public function getBit(row:uint, column:uint):uint {
			return ((_bitArray[row] as uint) >> column) & 1;
		}
		
		public function setBit(row:uint, column:uint, value:uint):void {
			var oldNum:int = getBit(row, column);
			
			_numSquares += (value-oldNum);
			
			if (value){
				_bitArray[row] |= (1 << (BOARD_WIDTH-column-1));
			}else {
				_bitArray[row] &= (~(1 << (BOARD_WIDTH-column-1)));
			}
		}
		
		public function putPiece(pieceIndex:int, direction:int, column:int, row:int):Boolean {
			var value:Array = PiecesDefination.PIECES_DEF[pieceIndex][direction];
			
			var width:uint = 0;
			var height:uint = 0;
			
			var orRes:uint = 0;
			for each(var n:uint in value) {
				orRes |= n;
				if (n != 0 ) {
					height++;
				}
			}
			while (orRes != 0) {
				width++;
				orRes >>= 1;
			}
			
			if (column + width - 1 > BOARD_WIDTH - 1 || row + height - 1 > BOARD_WIDTH - 1) {
				return false;
			}
			
			for (var dline:int = 0; dline < value.length; dline++ ) {
				var line:int = dline + row;
				if(line < BOARD_WIDTH){
					_bitArray[line] |= value[dline] << column;
				}
			}
			
			_numSquares += PiecesDefination.PIECES_NUM[pieceIndex];
			return true;
		}
		
		public function toString(name:String=""):String {
			var str:String = "";
			str+=("*******"+name+"*******\n");
			for (var i:int = 0; i < BOARD_WIDTH; i++ ) {
				//str+=(((_bitArray[i]|(1<<20)).toString(2) as String).+"\n");
				for (var j:int = 0;j< BOARD_WIDTH; j++) {
					str +=(((_bitArray[i]>>j)&1)?"■":"□");
				}
				str += "\n";
			}
			return str;
		}
		public function get clone():BitBoard {
			var newBoard:BitBoard = new BitBoard();
			for (var i:int = 0; i < BOARD_WIDTH; i++ ) {
				newBoard._bitArray[i] = _bitArray[i];
			}
			return newBoard;
		}
		public function and(b:BitBoard):BitBoard {
			var newBoard:BitBoard = new BitBoard();
			for (var i:int = 0; i < BOARD_WIDTH; i++ ) {
				newBoard._bitArray[i] = _bitArray[i] & b._bitArray[i];
			}
			return newBoard;
		}
		public function or(b:BitBoard):BitBoard {
			var newBoard:BitBoard = new BitBoard();
			for (var i:int = 0; i < BOARD_WIDTH; i++ ) {
				newBoard._bitArray[i] = _bitArray[i]|b._bitArray[i];
			}
			return newBoard;
		}
		
		public function sub(b:BitBoard):BitBoard {
			var newBoard:BitBoard = new BitBoard();
			for (var i:int = 0; i < BOARD_WIDTH; i++ ) {
				newBoard._bitArray[i] = _bitArray[i]&(~b._bitArray[i]);
			}
			return newBoard;
		}
		public function get isZero():Boolean {
			var res:uint = 0;
			for (var i:int = 0; i < BOARD_WIDTH; i++ ) {
				res|=_bitArray[i];
			}
			return res==0;
		}
		public function get extend():BitBoard {
			var newBoard:BitBoard = new BitBoard();
			for (var i:int = 1; i < BOARD_WIDTH-1; i++ ) {
				newBoard._bitArray[i] = (_bitArray[i]<<1)|(_bitArray[i]>>1)|_bitArray[i-1]|_bitArray[i+1];
			}
			newBoard._bitArray[0] = (_bitArray[0] << 1) | (_bitArray[0] >> 1) | _bitArray[1];
			newBoard._bitArray[BOARD_WIDTH-1] = (_bitArray[BOARD_WIDTH-1] << 1) | (_bitArray[BOARD_WIDTH-1] >> 1) | _bitArray[BOARD_WIDTH-2];
			return newBoard;
		}
		public function get angleExtend():BitBoard {
			var horExtBoard:BitBoard = new BitBoard();
			for (var i:int = 0; i < BOARD_WIDTH; i++ ) {
				horExtBoard._bitArray[i] = _bitArray[i]<<1|_bitArray[i]>>1;
			}
			horExtBoard = horExtBoard.sub(this);
			
			var verExtBoard:BitBoard = new BitBoard();
			for (i= 1; i < BOARD_WIDTH-1; i++ ) {
				verExtBoard._bitArray[i] |=(horExtBoard._bitArray[i-1]|horExtBoard._bitArray[i+1]);
			}
			verExtBoard._bitArray[0] |= horExtBoard._bitArray[1];
			verExtBoard._bitArray[BOARD_WIDTH - 1] |= horExtBoard._bitArray[BOARD_WIDTH - 2];
			verExtBoard = verExtBoard.sub(this.extend);
			return verExtBoard;
		}
		public function get numSquares():uint {
			return _numSquares;
		}
		
		/**
		 * return the width of the rightmost 1 and the leftmost 1
		 */
		public function get restrictWidth():uint {
			var orRes:uint = 0;
			for each(var n:uint in _bitArray) {
				orRes |= n;
			}
			
			var left:int = -1;
			var right:int = -1;
			
			while (orRes != 0) {
				if ((1==(orRes & 1))&&(-1==right)) {
					right = left;
				}
				orRes >>= 1;
				left++;
			}
			
			return left - right+1;
		}
		
		/**
		 * return the height of the rightmost 1 and the leftmost 1
		 */		
		public function get restrictHeight():uint {
			var top:int = -1;
			var btm:int = -1;
			for (var i:int = 0; i < BOARD_WIDTH;i++ ) {
				if (_bitArray[i] != 0) {
					btm = i;
					if (-1 == top) {
						top = i;
					}
				}
			}
			
			return btm - top+1;
		}
		
		public function moveDown():Boolean {
			if (0 != _bitArray[BOARD_WIDTH-1]) {
				return false;
			}
			
			for (var i:int = BOARD_WIDTH-1; i >0;i-- ) {
				_bitArray[i] = _bitArray[i-1];
			}
			
			_bitArray[0] = 0;
			
			return true;
		}
		
		public function moveUp():Boolean {
			if (0 != _bitArray[0]) {
				return false;
			}
			
			for (var i:int = 0; i < BOARD_WIDTH-1;i++ ) {
				_bitArray[i] = _bitArray[i+1];
			}
			
			_bitArray[BOARD_WIDTH-1] = 0;
			
			return true;
		}
		
		public function moveLeft():Boolean {
			var orRes:int = 0;
			for (var i:int = 1; i < _bitArray.length;i++ ) {
				orRes |= _bitArray[i];
			}
			if (orRes & 1) {
				return false;
			}
			
			for (i=0; i < _bitArray.length;i++ ) {
				_bitArray[i]>>=1;
			}
			return true;
		}
		
		public function moveRight():Boolean {
			var res:int = 0;
			for (var i:int = 1; i < _bitArray.length;i++ ) {
				res |= _bitArray[i];
			}
			if ((res>>(BitBoard.BOARD_WIDTH-1)) & 1) {
				return false;
			}
		
			for (i=0; i < _bitArray.length;i++ ) {
				_bitArray[i] <<= 1;
			}
			return true;
		}
	}

}